<template>
    <div class="uploader">
        <input ref="fileInputRef" type="file" :accept="accept" :multiple="isMultiple" @change="onChangeFileHandler" />
        <!-- <span class="upload-btn-wrap">
      <span class="upload-btn" @click="handleUpload">+ 上传素材</span>
    </span> -->
        <span @click="handleUpload">
            <slot>
                <div class="uploader-img">
                    <div class="uploader-icon"></div>
                    <div class="uploader-span">添加图片</div>
                </div>
            </slot>
        </span>
    </div>
</template>

<script>
import { defineComponent, ref, computed, watch, onMounted } from 'vue';
import createUploader from '@/lib/uploader-client';
import { getFileSuffix } from './utils/helper';
import { Toast } from 'vant';
import { v4 as uuid } from 'uuid';

const Status = {
    Prepare: 0,
    Processing: 1,
    Success: 2,
    Error: 3,
    Remove: 9
};

export default defineComponent({
    props: {
        // 限制上传的文件类型
        acceptTypes: {
            type: Array,
            default: () => ['jpg', 'jpeg', 'png', 'gif']
        },
        // 限制上传的文件大小
        acceptSize: {
            type: Number,
            default: 5 * 1024 * 1024 // 默认5M
        },
        // 是否允许上传多个文件
        isMultiple: {
            type: Boolean,
            default: false
        }
    },

    setup(props, { emit, expose }) {
        let uploadClient = null;

        let ToastNode = null;

        // allow upload file type
        const accept = computed(() => {
            if (props.acceptTypes.length) {
                return props.acceptTypes.map((ext) => '.' + ext).join(',');
            }

            return '*';
        });

        /* File Queue */
        const fileQueue = ref([]);
        watch(
            fileQueue,
            (queue) => {
                emit('update-file-list', queue);

                const isProcessing = queue.some(
                    (item) => item.status === Status.Processing
                );
                if (isProcessing) return;

                next();
            },
            { deep: true }
        );
        function next() {
            // next.
            const order = fileQueue.value.findIndex(
                (item) => item.status === Status.Prepare
            );
            const uploadFile = fileQueue.value[order];
            if (uploadFile) {
                uploadFile.status = Status.Processing;
                fileQueue.value.splice(order, 1, uploadFile); // update
                setTimeout(() => {
                    ToastNode = Toast.loading({
                        message: '上传中...',
                        forbidClick: true,
                        duration: 0
                    });
                }, 0);
                upload(uploadFile);
            }
        }
        // update upload file status in file queue.
        function updateUploadFileStatus(id, status, progress) {
            const order = fileQueue.value.findIndex((item) => item.id === id);
            const uploadFile = fileQueue.value[order];

            if (uploadFile) {
                // update
                switch (status) {
                    case Status.Remove:
                        if (uploadFile.status === Status.Processing) {
                            cancelUpload();
                        }
                        fileQueue.value.splice(order, 1);
                        return;

                    case Status.Processing:
                        uploadFile.progress = progress || 0;
                        break;
                }

                uploadFile.status = status;
                fileQueue.value.splice(order, 1, uploadFile);
            }
        }

        /* Upload */
        function upload(uploadFile) {
            const { file, id } = uploadFile;
            const callback = {
                onProgress,
                onSuccess,
                onError
            };

            emit('update-file', uploadFile);
            uploadClient.start(file, id, callback);
        }
        function cancelUpload() {
            if (uploadClient) {
                uploadClient.stop();
            }
        }

        function onProgress(payload) {
            const { id, progress } = payload;
            const percent = Math.floor(progress.percent * 100) / 100;

            // processing
            updateUploadFileStatus(id, Status.Processing, percent);
        }
        function onSuccess(payload) {
            const { id, data } = payload;
            // success
            updateUploadFileStatus(id, Status.Success);
            data.linkUrl = data.host + '/' + data.key
            emit('on-success', data, fileQueue.value);
            ToastNode && ToastNode.clear();
            // no need publish material??
        }
        function onError(payload) {
            const { id, info } = payload;
            // error
            updateUploadFileStatus(id, Status.Error);
            emit('on-error', info);

            // no permission
            if (info.code === 401) {
                uploadClient = null;
            }
            ToastNode && ToastNode.clear();
        }

        /* re-upload 重新上传 */
        async function reUploadByKey(id) {
            if (uploadClient == null) {
                uploadClient = await createUploader();
            }

            updateUploadFileStatus(id, Status.Prepare);
        }
        /* Remove 从上传队列中移除文件 */
        function removeUploadFileFromTheQueue(id) {
            updateUploadFileStatus(id, Status.Remove);
        }

        /* File Input */
        const fileInputRef = ref();
        function onChangeFileHandler(e) {
            const input = e.currentTarget;
            const files = input.files;
            if (files) {
                const uploadFileList = fileCheckAndFilter(files);
                if (uploadFileList) {
                    if (props.isMultiple) {
                        fileQueue.value =
                            fileQueue.value.concat(uploadFileList);
                    } else {
                        fileQueue.value = uploadFileList;
                    }
                }
            }
            input.value = '';
        }
        async function handleUpload() {
            if (uploadClient == null) {
                uploadClient = await createUploader();
            }
            fileInputRef.value.click();
        }

        /* File Check */
        // 检查文件类型和大小是否超出限制，不符合的文件将被过滤掉
        function fileCheckAndFilter(files) {
            if (files) {
                let fileList = Array.prototype.map.call(files, (file) => file);
                // check file type
                fileList = checkFileType(fileList);

                // check file size
                fileList = checkFileSize(fileList);

                return fileList.map((file) => genUploadFile(file));
            }
        }
        function checkFileType(files) {
            const limitType = props.acceptTypes;
            const errorTypeList = [];

            const fileList = files.filter((file) => {
                const fileType = getFileSuffix(file);
                if (limitType.includes(fileType)) {
                    return true;
                } else {
                    errorTypeList.push(file);
                    return false;
                }
            });

            if (errorTypeList.length) {
                emit('on-error', {
                    code: 1001,
                    file: errorTypeList
                });
            }

            return fileList;
        }
        function checkFileSize(files) {
            const limitSize = props.acceptSize;
            const errorSizeList = [];

            const fileList = files.filter((file) => {
                const fileSize = file.size;
                if (fileSize <= limitSize) {
                    return true;
                } else {
                    errorSizeList.push(file);
                    return false;
                }
            });

            if (errorSizeList.length) {
                emit('on-error', {
                    code: 1002,
                    file: errorSizeList
                });
            }

            return fileList;
        }
        // 生成待上传文件的结构
        function genUploadFile(file) {
            return {
                progress: 0,
                status: Status.Prepare,
                file,
                id: uuid()
            };
        }

        onMounted(() => {});

        expose({
            removeUploadFileFromTheQueue,
            reUploadByKey,
            handleUpload
        });

        return {
            fileInputRef,
            accept,
            onChangeFileHandler,
            handleUpload
        };
    }
});
</script>

<style lang="scss" scoped>
.uploader {
    display: inline-block;
    cursor: pointer;
    user-select: none;
    input[type='file'] {
        display: none;
    }
    .uploader-img {
        width: 82px;
        height: 82px;
        background: #f5f5f5;
        border-radius: 4px;
        text-align: center;
    }
    .uploader-icon {
        width: 18px;
        height: 18px;
        background-size: 18px 18px;
        background-position: 0 0;
        margin: 20px auto 6px;
        background: url(~@/assets/img/common/uploader.png) no-repeat center
            center;
    }
    .uploader-span {
        color: #a2a3a4;
        font-size: 12px;
    }
}
</style>
